'use strict'
const http = require('http');
const fs = require('fs');

const fsp = fs.promises;
//require在程序运行时，可以动态的导入模块
/*--------------------解析BODY数据-------------------*/
/**
 * Content-Disposition:form-data;name="file";filename="xxx"
 * Content-Type:text/plain
 * @param {string} headerData 
 */
function parseBodyHeaders(headerData){
    let hlist = headerData.split('\r\n').filter(p => p.length>0);
    //解析content-disposition => hlist[0]
    //按照;切分数组，选出从第二个元素开始的数组
    let csplit = hlist[0].split('; ').slice(1);
    let filename 
    let name
    for(let a of csplit){
        if(a.indexOf('filename') === 0){
            filename = a.substring(a.indexOf('=')+2,a.length-1);
        }else{
            name = a.substring(a.indexOf('=')+2,a.length-1);
        }
    }
    let headers = {};
    let k ;
    let ind = 0;
    for(let h of hlist){
        ind = h.indexOf(':');
        k = h.substring(0,ind).toLowerCase();
        headers[k] = h.substring(ind+1).trim();
    }
    return {
        filename,
        name,
        headers,
        contentType:headers['content-type'] || ''
    }
}

function parseBody(bodyData,headers){
    let ctype = headers['content-type'];
    let bdy = ctype.substring(ctype.indexOf('=') + 1);
    let crlf_bdy = `\r\n--${bdy}`;
    let header_end_index = bodyData.indexOf('\r\n\r\n',crlf_bdy.length);
    let headerData = bodyData.toString('utf8',crlf_bdy.length,header_end_index);

    //解析文件头信息
    let hd = parseBodyHeaders(headerData);
    let file = {
        start:header_end_index + 4,
        end:bodyData.indexOf(crlf_bdy,header_end_index),
        ...hd
    }
    file.length = file.end - file.start;
    return file;
}

//解析文件扩展名
function parseExtName(filename){
    if(filename.length < 2){
        return ''
    }
    let nsplit = filename.split('.').filter(p => p.length > 0);
    if(nsplit.length > 2){
        return ''
    }
    return `.${nsplit[nsplit.length-1]}`;
}
/*-------------------解析BODY数据----end ---------------*/
let html = `
    <!DOCTYPE html>
    <html>
        <head>
            <title>First HTTP</title>
        </head>
        <body>
            你好。
        </body>
    </html>
`;

let routerTable = {
    GET:{
        //GET是获取信息的方法
        '/':async(ctx) => {
            ctx.response.setHeader('content-type','text/html;charset=utf-8');
            ctx.response.end(html);
        },
        '/test':async(ctx) => {
            let status = parseInt(Math.random() * 200) + 199;
            ctx.response.statusCode = status;
            ctx.response.end('test end');
        },
        '/upload' : async (ctx) => {
            let stm = fs.createReadStream('./upload.html');
            //创建一个html的可读流
            stm.pipe(ctx.response,{end:false});//创建一个upload.js和upload.html两个文件之间的流联系
            stm.on('end',() => {ctx.end();});
        }
    },
    POST:{
        //POST是提交信息的方法
        '/data':async(ctx) => {
            let file = parseBody(ctx.rawBody,ctx.headers);
            console.log(file);
            if(!ctx.rawBody){
                ctx.end();
                return;
            }
            let fname = `${Date.now()}-${Math.random()}` + `${parseExtName(file.filename)}`
            let fh = await fsp.open(fname,'w+');
            let fret = await fh.write(ctx.rawBody,file.start,file.length,0);

            //关闭打开的文件
            fh.close();

            ctx.end(`${fret.bytesWritten}`);
            //ctx.end(ctx.rawBody);
        }
    },
}

http.createServer(async(request,response) => {
    let rm = routerTable[request.method];
    let usplit = request.url.split('?');
    let pathname = usplit[0];
    let querystring = usplit[1] || '';
    //将请求的方法当作属性名传入routerTable
    if(!rm || !rm[request.url]){
        response.statusCode = 404;
        response.end('page not found');
        return ;
    }
    //ctx就是context 意为请求上下文
    //是一个打包了各种数据的集合
    let ctx = {
        method:request.method,
        path:pathname,
        search:querystring,
        headers:request.headers,
        response:response,
        end:(data,encoding = 'utf8') => {
            response.end(data,encoding)
        },
        write:(data,encoding = 'utf8') => {
            response.write(data,encoding)
        },
        totalLength:0,
        rawBody:null,
        bufferList:[]
    }

    await new Promise((rv,rj) => {
        if('PD'.indexOf(request.method[0]) >= 0){
            request.on('data',chunk => {
                ctx.bufferList.push(chunk);
                ctx.totalLength += chunk.length;
            })
        }else{
            request.on('data',data => {
                response.statusCode = 400;
                response.end('bad request',() => {
                    request.destroy();
                });
            })
        }
        let resolved = false;
        request.on('end',() => {
            resolved = true;
            rv();
        });
        
        request.on('close',() => {
            (!resolved) && rv();
        })
    })

    if(!response.writable || response.writableEnded){
        return 
    }

    if(ctx.bufferList.length>0){
        ctx.rawBody = Buffer.concat(ctx.bufferList,ctx.totalLength);
        ctx.bufferList = [];
    }

    rm[pathname](ctx)
}).listen(3456); 

/*各种格式的对照情况
    .jpg .jpeg -> image/jpeg
    .png -> image/png
    .gif -> image/gif
    .ico -> image/x-icon
    .txt -> text/plain
    .json -> text/json 或 application/json
    .xml -> text/xml 或 application/xml
 */
//setHeader构造协议头，以包的形式一起发送出去
//text/html服务器告诉浏览器，这是一个html文件，以Html文件的形式解析
//charset=utf-8告诉浏览器文字的格式是utf8的形式，这样可以翻译出中文